#include "SceneManager.h"
#include <time.h>

int SceneManager::theta_x;
int SceneManager::theta_y;
int SceneManager::crt_render_mode;
int SceneManager::crt_shape;
int SceneManager::crt_rs;
int SceneManager::crt_vs;
double SceneManager::cyl_height = 3;
double SceneManager::cyl_ray = 1;
double SceneManager::sph_ray = 3;
const float SceneManager::rotateAroundOwnAxisAmount = (float)5/180*PI;
bool SceneManager::bUpperT = false;
bool SceneManager::bLowerT = false;
bool SceneManager::bUpperR = false;
bool SceneManager::bLowerR = false;
bool SceneManager::bUpperS = false;
bool SceneManager::bLowerS = false;
MHouse * SceneManager::house = NULL;
MSphere *SceneManager::sphere = NULL;
MTorus *SceneManager::donut = NULL;
MCylinder * SceneManager::cylinder = NULL;
MCone *SceneManager::cone = NULL;
MMesh *SceneManager::monster = NULL;
MMesh *SceneManager::dragon = NULL;
MMesh *SceneManager::teapot = NULL;
MCube *SceneManager::cube = NULL;
Shape *SceneManager::shape = NULL;
MCamera * SceneManager::camera = NULL;
static const float TAmount = 0.05;
static const float RAmount = 3;
static const float RCamAmount = 3;
static const float SAmount = 1.1;
static const float RSAmount = 1/SAmount;
static const float ZoomOut = 1.1;
static const float ZoomIn = 1/ZoomOut;

template <class T>
void DELETEO(T *x) {
	if(x) 
		delete x;
}

SceneManager::~SceneManager(void)
{

}

void SceneManager::my_setup() 
{
	theta_x = 0;
	theta_y = 0;
	crt_render_mode = GL_LINE_LOOP;
	crt_shape = HOUSE;
	crt_rs = 20;
	crt_vs = 10;
	sphere = MSphere::makeSphere(1,10,10);
	cylinder = MCylinder::makeCylinder(1,1,10,10);
	cube = MCube::makeCube(1,4);
	donut = MTorus::makeTorus(1.2,0.2,40,15);
	cone = MCone::makeCone(1,2,2,10);
	monster = MMesh::makeMesh("monsterdance.obj");
	dragon = MMesh::makeMesh("dragon.obj");
	teapot = MMesh::makeMesh("teapot.obj");
	house = new MHouse();
	camera = new MCamera(MVertex(0,5,25),MVertex(),MVertex(0,1,0));
	shape = house;
	return;
}

int SceneManager::my_glmain(int argc, char **argv) {

	glutInit( &argc, argv ) ;

	/* we define these setup procedures */
	my_glutsetup() ;  
	my_glsetup() ;
	my_setup();

	/* go into the main event loop */
	glutMainLoop() ;

	return(0) ;
}

void  SceneManager::my_glutsetup() {
	/* specify display mode -- here we ask for a double buffer and RGB coloring */
	/* NEW: tell display we care about depth now */
	glutInitDisplayMode (GLUT_DOUBLE |GLUT_RGB |GLUT_DEPTH);

	/* make a 400x400 window with the title of "Stitcher" placed at the top left corner */
	glutInitWindowSize(400,400);
	glutInitWindowPosition(0,0);
	glutCreateWindow("Assignment 3");

	/*initialize callback functions */
	glutDisplayFunc( my_display );
	glutReshapeFunc( my_reshape ); 
	glutMouseFunc( my_mouse);
	glutKeyboardFunc(my_keyboard);
	glutKeyboardUpFunc(my_keyboardup);

	/*just for fun, uncomment the line below and observe the effect :-)
	Do you understand now why you shd use timer events, 
	and stay away from the idle event?  */
	//glutIdleFunc( my_idle );
	return ;
}

void  SceneManager::my_glsetup() {
	/* specifies a background color: black in this case */
	glClearColor(0,0,0,0) ;

	/* NEW: now we have to enable depth handling (z-buffer) */
	glEnable(GL_DEPTH_TEST);

	/* NEW: setup for 3d projection */
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity();
	// perspective view
	gluPerspective( 20.0, 1.0, 1.0, 100.0);
	return;
}

void SceneManager::my_display()
{
	/* clear the buffer */
	/* NEW: now we have to clear depth as well */
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) ;

	glMatrixMode(GL_MODELVIEW) ;
	glLoadIdentity();
	camera->updateGluLookAt();

	//draw world axis
	static const float axisLen = 3;
	float thickness=2.0;
	glLineWidth(thickness);
	draw_line(MVertex(0,0,0),MVertex(axisLen,0,0),1);
	draw_line(MVertex(0,0,0),MVertex(0,axisLen,0),1);
	draw_line(MVertex(0,0,0),MVertex(0,0,axisLen),1);
	glLineWidth(1.0);
	switch(crt_shape){
	case HOUSE: 
		if(house) {
			house->drawHouse(draw_triangle,draw_line);	
		}
		break;
	case CUBE: 
		if(cube) {
			cube->drawCube(draw_poly,draw_line);
		}
		break; /*TODO: replace with draw_cube_smart*/
	case CYLINDER: 
		if(cylinder) {
			cylinder->drawCylinder(draw_triangle,draw_line);
		}
		break;
	case CONE: 
		if(cone) {
			cone->drawCone(draw_triangle,draw_line);
		}
		break;
	case SPHERE:
		if(sphere) {
			sphere->drawSphere(draw_triangle,draw_line);  
			if(!sphere->isComplete())
				glutTimerFunc( 10, my_timeout, 1);
		}
		break;
	case TORUS: 
		if(donut) {
			donut->drawTorus(draw_triangle,draw_line);  
			if(!donut->isComplete())
				glutTimerFunc( 10, my_timeout, 1);
		}
		break;
	case MESH1: 
		if(monster) {
			monster->drawMesh(draw_poly, draw_line);
		}
		break;
	case MESH2: 
		if(teapot) {
			teapot->drawMesh(draw_poly, draw_line);
		}
		break;
	case MESH3: 
		if(dragon) {
			dragon->drawMesh(draw_poly, draw_line);
		}
		break;
	default: break;
	}

	/* buffer is ready */
	glutSwapBuffers();

	return ;
}


void SceneManager::my_timeout(int id)
{
	switch (id) {
	case 1:	//event id 1, sphere
		if(shape && shape->bRotateAroundOwnAxis) {
			shape->rotateAroundOwnAxis(rotateAroundOwnAxisAmount);
			glutPostRedisplay();
			glutTimerFunc( 100, my_timeout, 1);
		}
		break;
	}
}

void SceneManager::my_keyboardup(unsigned char key, int x, int y) {
	switch (key) {
	case 't':
		bLowerT = false;
		break;
	case 'T':
		bUpperT = false;
		break;
	case 's':
		bLowerS = false;
		break;
	case 'S':
		bUpperS = false;
		break;
	case 'r':
		bLowerR = false;
		break;
	case 'R':
		bUpperR = false;
		break;
	}
}


void SceneManager::my_keyboard(unsigned char key, int x, int y)
{
	switch( key ) {
	case 'y':
	case 'Y': 
		{
			if(bUpperT) {
				shape->translate(0,TAmount,0);
			}
			else if(bLowerT) {
				shape->translate(0,-TAmount,0);
			}
			else if(bUpperR) {
				shape->rotate(RAmount,0,1,0);
			}
			else if(bLowerR) {
				shape->rotate(-RAmount,0,1,0);
			}
			else if(bUpperS) {
				shape->scale(1,SAmount,1);
			}
			else if(bLowerS) {
				shape->scale(1,RSAmount,1);
			}
			glutPostRedisplay(); 
		}; break;
	case 'x':
	case 'X': 
		{
			if(bUpperT) {
				shape->translate(TAmount,0,0);
			}
			else if(bLowerT) {
				shape->translate(-TAmount,0,0);
			}
			else if(bUpperR) {
				shape->rotate(RAmount,1,0,0);
			}
			else if(bLowerR) {
				shape->rotate(-RAmount,1,0,0);
			}
			else if(bUpperS) {
				shape->scale(SAmount,1,1);
			}
			else if(bLowerS) {
				shape->scale(RSAmount,1,1);
			}
			glutPostRedisplay(); 
		}; 
		break;
	case 'z':
	case 'Z':
		{
			if(bUpperT) {
				shape->translate(0,0,TAmount);
			}
			else if(bLowerT) {
				shape->translate(0,0,-TAmount);
			}
			else if(bUpperR) {
				shape->rotate(RAmount,0,0,1);
			}
			else if(bLowerR) {
				shape->rotate(-RAmount,0,0,1);
			}
			else if(bUpperS) {
				shape->scale(1,1,SAmount);
			}
			else if(bLowerS) {
				shape->scale(1,1,RSAmount);
			}
			glutPostRedisplay(); 
		}
		break;
	case '1': {
		crt_shape = CUBE;
		shape = cube;
		glutPostRedisplay();
			  }; break;
	case '2':
		crt_shape = SPHERE;
		shape = sphere;
		glutPostRedisplay();
		break;
	case '3':
		crt_shape = CYLINDER;
		shape = cylinder;
		glutPostRedisplay();
		break;
	case '4':
		crt_shape = CONE;
		shape = cone;
		glutPostRedisplay();
		break;
	case '5':
		crt_shape = TORUS;
		shape = donut;
		glutPostRedisplay();
		break;
	case '6':
		crt_shape = HOUSE;
		shape = house;
		glutPostRedisplay();
		break;
	case '7':
		crt_shape = MESH1;
		shape = monster;
		glutPostRedisplay();
		break;
	case '8':
		crt_shape = MESH2;
		shape = teapot;
		glutPostRedisplay();
		break;
	case '9':
		crt_shape = MESH3;
		shape = dragon;
		glutPostRedisplay();
		break;
	case 'q': 
	case 'Q':
		cleanupExit();
	case 't':
		bLowerT = true;
		break;
	case 'T':
		bUpperT = true;
		break;
	case 's':
		bLowerS = true;
		break;
	case 'S':
		bUpperS = true;
		break;
	case 'r':
		bLowerR = true;
		break;
	case 'R':
		bUpperR = true;
		break;
	case 'P':
		camera->translate(TAmount,0,0);
		glutPostRedisplay();
		break;
	case 'p':
		camera->translate(-TAmount,0,0);
		glutPostRedisplay();
		break;
	case 'N':
		camera->translate(0,0,TAmount);
		glutPostRedisplay();
		break;
	case 'n':
		camera->translate(0,0,-TAmount);
		glutPostRedisplay();
		break;
	case 'V':
		camera->translate(0,TAmount,0);
		glutPostRedisplay();
		break;
	case 'v':
		camera->translate(0,-TAmount,0);
		glutPostRedisplay();
		break;
	case 'J'://camera roate around x axis
		camera->rotate(RCamAmount,1,0,0);
		glutPostRedisplay();
		break;
	case 'j'://camera roate around x axis
		camera->rotate(-RCamAmount,1,0,0);
		glutPostRedisplay();
		break;
	case 'K'://camera roate around y axis
		camera->rotate(RCamAmount,0,1,0);
		glutPostRedisplay();
		break;
	case 'k'://camera roate around y axis
		camera->rotate(-RCamAmount,0,1,0);
		glutPostRedisplay();
		break;
	case 'L'://camera roate around z axis
		camera->rotate(RCamAmount,0,0,1);
		glutPostRedisplay();
		break;
	case 'l'://camera roate around z axis
		camera->rotate(-RCamAmount,0,0,1);
		glutPostRedisplay();
		break;
	case 'W'://camera zoom out
		camera->scale(ZoomOut,ZoomOut,ZoomOut);
		glutPostRedisplay();
		break;
	case 'w'://camera zoom in
		camera->scale(ZoomIn,ZoomIn,ZoomIn);
		glutPostRedisplay();
		break;
	case 'O':
	case 'o':
		DELETEO(sphere);
		DELETEO(donut);
		DELETEO(cone);
		DELETEO(cube);
		DELETEO(cylinder);
		DELETEO(monster);
		DELETEO(dragon);
		DELETEO(teapot);
		DELETEO(house);
		DELETEO(camera);
		sphere = MSphere::makeSphere(1,10,10);
		cylinder = MCylinder::makeCylinder(1,1,10,10);
		cube = MCube::makeCube(1,4);
		donut = MTorus::makeTorus(1.2,0.2,40,15);
		cone = MCone::makeCone(1,2,2,10);
		house = new MHouse();
		monster = MMesh::makeMesh("monsterdance.obj");
		dragon = MMesh::makeMesh("dragon.obj");
		teapot = MMesh::makeMesh("teapot.obj");
		camera = new MCamera(MVertex(0,5,25),MVertex(),MVertex(0,1,0));
		shape = house;
		crt_shape = HOUSE;
		glutPostRedisplay();
		break;
	case '+':	//camera rotate around object's central axis
		if(shape) {
			//camera->lookAt = shape->v1.midPoint(shape->v2);
			camera->rotateAroundAxis(shape->v1,shape->v2,RCamAmount);
			glutPostRedisplay();
		}
		break;
	case '-':	//camera rotate around object's central axis
		if(shape) {
			//camera->lookAt = shape->v1.midPoint(shape->v2);
			camera->rotateAroundAxis(shape->v1,shape->v2,-RCamAmount);
			glutPostRedisplay();
		}
		break;
	default: break;
	}

	return ;
}

void SceneManager::my_idle(void) {
	theta_y = (theta_y+2) %360;
	glutPostRedisplay();
	return ;
}

void SceneManager::my_reshape(int w, int h) {
	/* define viewport -- x, y, (origin is at lower left corner) width, height */
	glViewport (0, 0, min(w,h), min(w,h));

	return;
}


void SceneManager::my_mouse(int b, int s, int x, int y) {
	switch( b ) {

	case GLUT_LEFT_BUTTON:
		if( s == GLUT_DOWN ) {
			if(shape) {
				shape->bRotateAroundOwnAxis = !shape->bRotateAroundOwnAxis; 	
				if(shape->bRotateAroundOwnAxis) {
					glutTimerFunc( 100, my_timeout, 1);
				}
			}
		}
		if( s == GLUT_UP ) {
		} 
		break ;

	case GLUT_RIGHT_BUTTON:
		if ( s == GLUT_DOWN ) {
			if(shape) {
				shape->bNormal = !shape->bNormal;
			}
			glutPostRedisplay();
		}
		if( s == GLUT_UP ) {
		}
		break ;
	}

	return ;
}

void SceneManager::draw_triangle(const GLfloat vertices[][4], int iv1, int iv2, int iv3, int ic) {
	glBegin(crt_render_mode); 
	{
		glColor3fv(colors[ic]);
		/*note the explicit use of homogeneous coords below: glVertex4f*/
		glVertex4fv(vertices[iv1]);
		glVertex4fv(vertices[iv2]);
		glVertex4fv(vertices[iv3]);
	}
	glEnd();
}

void SceneManager::draw_triangle(const MVertex &p1,  const MVertex &p2, const MVertex &p3, int ic) {
	glBegin(crt_render_mode); 
	{
		glColor3fv(colors[ic]);
		/*note the explicit use of homogeneous coords below: glVertex4f*/
		glVertex4fv(p1.point);
		glVertex4fv(p2.point);
		glVertex4fv(p3.point);
	}
	glEnd();
}

void SceneManager::draw_line(const MVertex &p1,  const MVertex &p2,int ic) {
	glBegin(GL_LINES); 
	{
		glColor3fv(colors[ic]);
		/*note the explicit use of homogeneous coords below: glVertex4f*/
		glVertex4fv(p1.point);
		glVertex4fv(p2.point);
	}
	glEnd();
}

void SceneManager::draw_poly(const vector<MVertex> &v1, int ic) {
	glBegin(crt_render_mode); 
	{
		glColor3fv(colors[ic]);
		for(unsigned int i =0; i<v1.size();i++) {
		/*note the explicit use of homogeneous coords below: glVertex4f*/
		glVertex4fv(v1[i].point);
		}
	}
	glEnd();
}

void SceneManager::cleanupExit() {
	DELETEO(sphere);
	DELETEO(donut);
	DELETEO(cone);
	DELETEO(cube);
	DELETEO(cylinder);
	DELETEO(monster);
	DELETEO(dragon);
	DELETEO(teapot);
	DELETEO(house);
	DELETEO(camera);
	exit(0);
}








